#!/usr/bin/env python
"""Command engine binds the cmdmgr with the process/thread
manager according to the command-line options.
"""
#-IMPORTS-----------------------------------------------------------------------
import sys
import cmdobj
import logging
#import procmgr
import cmdline
import constants
import cmdmgr
import opermgr
import threadmgr
import datastruct
import cmdfactory
#-CONSTANTS---------------------------------------------------------------------
IP_DELIMITER = ','
#-CLASSES-----------------------------------------------------------------------
class CmdEngine:
    def __init__(self):
        self.__log      = logging.getLogger(__name__)
        self.__cmgr     = cmdmgr.CmdManager()
        self.__cf       = cmdfactory.CmdFactory()
        self.__omgr     = opermgr.OperationsManager(self.__cf)
        self.__emgr     = None
        self.__sc       = {}
        self.__sc_txt   = ''
        self.__data     = {}
        self.__x_ips    = cmdline.options.xip.split(IP_DELIMITER)
        self.__ips      = cmdline.options.ip.split(IP_DELIMITER)
        self.__load_data()

    def __load_data(self):
        """Loads the sysconf.json file into memory."""
        self.__log.debug('Loading %s, please wait...' % cmdline.options.dataPath)
        try:
            self.__sc_txt = open(cmdline.options.dataPath).read()
        except:
            self.__log.error('Cannot open/read %s, exiting!' %
                             cmdline.options.dataPath)
            sys.exit(-1)
        #try:
        #    self.__sc = json.read(self.__sc_txt)
        #except:
        #    self.__log.error('Cannot parse the json text, exiting!')
        #    sys.exit(-1)
        self.__data = datastruct.load_json(self.__sc)

    def __printOptions(self):
        self.__log.info(cmdline.getOptionsString())

    def __printRefSys(self, p_ref = None, p_cnt = 1):
        if p_ref is None:
            return None
        for computer in p_ref['computers']:
            if computer.has_key('desc'):
                self.__log.info('-'*p_cnt+computer['name']+'='+computer['desc'])
            else:
                self.__log.info('-'*p_cnt+computer['name'])
            if computer.has_key('mac_addr'):
                self.__log.info('-'*(p_cnt+1)+'mac_addr:'+computer['mac_addr'])
            if computer.has_key('addr'):
                self.__log.info('-'*(p_cnt+1)+'addr')
                for k, v in computer['addr'].items():
                    self.__log.info('-'*(p_cnt+2)+v+'='+k)
            if computer.has_key('apps'):
                self.__log.info('-'*(p_cnt+1)+'apps')
                for k, v in computer['apps'].items():
                    self.__log.info('-'*(p_cnt+3)+k)
                    if type(v) == type({}):
                        if v.has_key('deploy'):
                            self.__log.info('-'*(p_cnt+4)+'deploy='+v['deploy'])
                        if v.has_key('env'):
                            self.__log.info('-'*(p_cnt+4)+'env='+v['env'])
                        if v.has_key('scripts'):
                            self.__log.info('-'*(p_cnt+4)+'scripts')
                            for ky, vl in v['scripts'].items():
                                self.__log.info('-'*(p_cnt+5)+ky+'='+vl)
                    else:
                        self.__log.info('-'*(p_cnt+2)+k)

    def __printSysConf(self, p_sc):
        if p_sc.has_key('group'):
            self.__log.info('group=%s'%(p_sc['group']))
        for ref in p_sc['refsys']:
            cnt = 1
            self.__log.info('-'*cnt+ref['name']+'='+ref['desc'])
            cnt += 1
            self.__log.info('-'*cnt+'computers')
            cnt += 1
            self.__printRefSys(ref, cnt)

    def __getRefIps(self, p_refname = ''):
        indx = self.__getRefIndex(p_refname)
        if indx is None:
            return None
        t_ips = []
        for computer in self.__sc['refsys'][indx]['computers']:
            for name, ip in computer['addr'].items():
                t_ips.append(ip)
        return t_ips

    def __ipExists(self, p_ip = ''):
        if p_ip not in self.__x_ips:
            if p_ip in self.__sc_txt:
                return True
        self.__log.error('%s does not exist in %s'%
                         (p_ip, cmdline.options.dataPath))
        return False

    def __printArgv(self):
        t_buf = ''
        for arg in sys.argv:
            t_buf += ' %s'%arg
        self.__log.info(t_buf)

    def __confirm(self):
        self.__log.info('Do you want to run this configuration (y/n)? ')
        response = raw_input()
        if response in ['n', 'N']:
            self.__log.info('User response was NO, goodbye!')
            return False
        self.__log.info('User response was YES, march on...')
        return True

    def __checkOptions(self):
        if cmdline.options.ip == '' and cmdline.options.op != constants.OP_LISTALL:
            found = False
            for ref_name in self.__sc['refsys']:
                if ref_name['name'] == cmdline.options.ref:
                    found = True
            if not found:
                self.__log.error('No reference named (%s) exists in (%s), exiting.'
                                 %(cmdline.options.ref,
                                   cmdline.options.dataPath))
                return False
        if cmdline.options.op not in self.__omgr.ops:
            self.__log.error('(%s) not a known command, use -c (--operations) \
to see the known operations, exiting!' % (cmdline.options.op))
            return False
        return True

    def __createRequest(self, p_ip):
        if self.__ipExists(p_ip):
            n_cmd_params = cmdobj.CMD_PARAMS
            if cmdline.op_options != '':
                n_cmd_params[cmdobj.ARGS] = cmdline.op_options
            elif self.__omgr.ops[cmdline.options.op] != '':
                n_cmd_params[cmdobj.ARGS]= self.__omgr.ops[cmdline.options.op]
            else:
                n_cmd_params[cmdobj.ARGS] = ''
            n_cmd_params[cmdobj.IP] = p_ip
            n_cmd = self.__cf.getCmd(cmdline.options.op, n_cmd_params)
            return self.__cmgr.addRequest(n_cmd)
        return False

    def __createRequests(self):
        if self.__ips[0] != '': #Handle the -i parameter
            for t_ip in self.__ips:
                self.__createRequest(t_ip)
        elif cmdline.options.ref != '': #Handle the -r parameter
            t_ip_list = self.__getRefIps(cmdline.options.ref)
            for t_ip in t_ip_list:
                self.__createRequest(t_ip)
        if len(self.__cmgr.waiting) < 1:
            return False
        return True

    def __getRefIndex(self, p_ref_name = None):
        if p_ref_name is None:
            return None
        for i in range(len(self.__sc['refsys'])):
            if self.__sc['refsys'][i]['name'] == p_ref_name:
                return i
        return None

    def execute(self):
        if not self.__checkOptions():
            return False
        if cmdline.options.verbose:
            self.__printOptions()
        self.__printArgv()
        if not cmdline.options.force:
           if not self.__confirm():
               return False
        if cmdline.options.op == constants.OP_LISTALL:
            self.__printSysConf(self.__sc)
            return True
        elif cmdline.options.op == constants.OP_LIST:
            self.__log.info('Reference System: %s' % cmdline.options.ref)
            indx = self.__getRefIndex(cmdline.options.ref)
            if indx is not None:
                self.__printRefSys(self.__sc['refsys'][indx])
            return True
        elif cmdline.options.known:
            self.__log.info('Known operations: %s' % self.getOperations())
            return True
        if self.__createRequests():
            if cmdline.options.threaded:
                self.__emgr = threadmgr.ThreadManager(self.__cmgr)
            else:
                self.__emgr = procmgr.ProcessManager(self.__cmgr)
            self.__emgr.run()
            return True
        else:
            self.__log.error('No command requests were generated, exiting...')
        return False

    def getOperations(self):
        return self.__omgr.ops
